嗨我是k66,延續前篇Day24續寫Entry.c,前篇卡讀使用者按鍵,我發現問題在於WaitForEvent(),其實在while(1)內可以不用Wait,僅使用ReadKeyStroke()就能達成讀使用者按鍵。一樣先放成果圖,再放程式碼。
按B進入Boot Menu(Boot Menu製作細節參考Day23)
放碼上來!
受限於篇幅,本篇僅放完整Entry.c
,其他檔案請參考前篇Day24。
Entry.c
#include<Entry.h>
EFI_STATUS ConvertBmpToBlt(IN VOID *BmpImage, IN UINTN BmpImageSize, IN OUT VOID **GopBlt, IN OUT UINTN *GopBltSize, OUT UINTN *PixelHeight, OUT UINTN *PixelWidth)
{
UINT8 *ImageData;
UINT8 *ImageBegin;
BMP_IMAGE_HEADER *BmpHeader;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
UINT64 BltBufferSize;
UINTN Height;
UINTN Width;
UINTN ImageIndex;
BmpHeader = (BMP_IMAGE_HEADER *)BmpImage;
ImageBegin = ((UINT8 *)BmpImage) + BmpHeader->ImageOffset;
if (BmpHeader->BitPerPixel != 24 && BmpHeader->BitPerPixel != 32)
return EFI_UNSUPPORTED;
BltBufferSize = BmpHeader->PixelWidth * BmpHeader->PixelHeight * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
*GopBltSize = (UINTN)BltBufferSize;
*GopBlt = AllocatePool(*GopBltSize);
if (*GopBlt == NULL)
{
return EFI_OUT_OF_RESOURCES;
}
*PixelWidth = BmpHeader->PixelWidth;
*PixelHeight = BmpHeader->PixelHeight;
ImageData = ImageBegin;
BltBuffer = *GopBlt;
for (Height = 0; Height < BmpHeader->PixelHeight; Height++)
{
Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth];
for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Blt++)
{
switch (BmpHeader->BitPerPixel)
{
case 24:
Blt->Blue = *ImageData++;
Blt->Green = *ImageData++;
Blt->Red = *ImageData++;
break;
case 32:
ImageData++;
Blt->Blue = *ImageData++;
Blt->Green = *ImageData++;
Blt->Red = *ImageData++;
break;
default:
break;
}
}
ImageIndex = (UINTN)(ImageData - ImageBegin);
if ((ImageIndex % 4) != 0)
ImageData = ImageData + (4 - (ImageIndex % 4));
}
return EFI_SUCCESS;
}
void ShowCurrTime()
{
UINT8 sec=0;
UINT8 min=0;
UINT8 hour=0;
// UINT8 weekday=0;
UINT8 day=0;
UINT8 month=0;
UINT8 year=0;
IoWrite8(CMOS_ADDRESS,0x00);// sec, 0x00
sec=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x02);// min, 0x02
min=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x04);// hour, 0x04
hour=IoRead8(CMOS_DATA);
// IoWrite8(CMOS_ADDRESS,0x06);// weekday, 0x06
// weekday=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x07);// day, 0x07
day=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x08);// month, 0x08
month=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x09);// year, 0x09
year=IoRead8(CMOS_DATA);
Print(L"| Curr time: %02x/%02x/%02x %02x:%02x:%02x |\n",year,month,day,hour,min,sec);
}
void ShowGraphicMode()
{
Print(L"| Graphic Mode: 1920x1080 |\n");
}
static void BootMenu()
{
// 設定字為red(0x04),並且清除畫面
gST -> ConOut -> SetAttribute(gST->ConOut,0x4);
gST -> ConOut -> ClearScreen(gST->ConOut);
Print(L"*******************************\n");
Print(L"| Boot Menu |\n");
Print(L"|-----------------------------|\n");
Print(L"| |\n");
Print(L"| Press 1 into Kernel 1 |\n");
Print(L"| |\n");
Print(L"| Press 2 into Kernel 2 |\n");
Print(L"| |\n");
Print(L"*******************************\n");
// int (*KernelEntry)();
// KernelEntry = (int (*)() )KernelEntryPoint;
// Print(L"Jump to Kernel Entry Point");
// int KN = KernelEntry();
// Print(L"Kernel Entry Value = %d \n", KN);
// Select one of all kernels
EFI_INPUT_KEY key;
gST->ConIn->ReadKeyStroke(gST->ConIn, &key);
while(1)
{
gST->ConIn->ReadKeyStroke(gST->ConIn, &key);
if(key.UnicodeChar == L'1' || key.UnicodeChar == L'2' )
{
gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
Print(L"[Press B] ENTER BOOT MENU...\n");
gST -> ConOut -> SetAttribute(gST->ConOut,0x4); // 設定為紅字
if(key.UnicodeChar == L'1')
Print(L"Entry Kernel 1....");
else
Print(L"Entry Kernel 2....");
// Kernel(key.UnicodeChar);
break;
}
}
}
void ShowHomeMenu()
{
// 清除畫面並且設定字為白色(0x0F)
gST -> ConOut -> ClearScreen(gST->ConOut);
gST -> ConOut -> SetAttribute(gST->ConOut,0x0F);
Print(L"*********************************\n");
Print(L"| OinkBootLoader v0.0 |\n");
Print(L"| CopyRight 2023 k66 |\n");
Print(L"| |\n");
Print(L"| |\n");
Print(L"|-------------------------------|\n");
ShowCurrTime();
ShowGraphicMode();
Print(L"| UEFI Version: 2.9 |\n");
Print(L"|-------------------------------|\n");
Print(L"| Press B for BOOT MENU |\n");
Print(L"| Press S for SETUP |\n");
Print(L"| Press Q for SHUTDOWN |\n");
Print(L"*********************************\n");
}
EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE* SystemTable)
{
EFI_STATUS Status;
EFI_GRAPHICS_OUTPUT_PROTOCOL * GraphicsProtocol = NULL;
// EFI_GRAPHICS_OUTPUT_BLT_PIXEL BLACK = { 0x00, 0x00, 0x00, 0 };
// UINTN EventIndex;
// EFI_EVENT KeyEvent;
// EFI_EVENT WaitList[2];
EFI_INPUT_KEY Key;
UINTN x=600, y=100; //欲顯示Logo.bmp的左上角x,y座標
VOID *GopBlt = NULL;
UINTN GopBltSize;
UINTN BmpHeight;
UINTN BmpWidth;
EFI_FILE_PROTOCOL *Root = 0;
EFI_FILE_PROTOCOL *NewHandle = 0;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
EFI_FILE_INFO *fileinfo;
VOID * Buffer;
UINTN BufferSize;
// IMAGE * ImageList = NULL;
UINTN infosize = SIZE_OF_EFI_FILE_INFO;
EFI_GUID info_type = EFI_FILE_INFO_ID;
/* 檔案處理 */
/* 四步: 1.LocateProtocol 2.OpenVolume 3.Image Open 4.AllocatePool */
Status = gBS->LocateProtocol(&gEfiSimpleFileSystemProtocolGuid, NULL, (VOID**)&SimpleFileSystem);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): LocateProtocol\n");
return Status;
}
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): OpenVolume \n");
return Status;
}
Status = Root->Open(Root, &NewHandle, (CHAR16*)L"Logo.bmp", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Image Open \n");
return Status;
}
Status = gBS->AllocatePool(AllocateAnyPages, infosize, (VOID **)&fileinfo);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): AllocatePool \n");
return Status;
}
Status = NewHandle->GetInfo(NewHandle, &info_type, &infosize, NULL);
Status = NewHandle->GetInfo(NewHandle, &info_type, &infosize, fileinfo);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Get Informations\n");
return Status;
}
/* Read Buffer&BufferSize for converting BMP to Blt */
BufferSize = fileinfo->FileSize;
gBS->AllocatePool(AllocateAnyPages, BufferSize, (VOID **)&Buffer);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): AllocatePool\n");
return Status;
}
/* Buffer處理 */
Status = NewHandle->Read(NewHandle, &BufferSize, Buffer);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Read Buffer\n");
return Status;
}
/* 關閉檔案 */
Status = NewHandle->Close(NewHandle);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Close \n");
return Status;
}
/* Convert BMP to Blt */
Status = ConvertBmpToBlt(Buffer, BufferSize, &GopBlt, &GopBltSize, &BmpHeight, &BmpWidth);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): ConvertBmpToBlt\n");
return Status;
}
/* GraphicsProtocol */
Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID**)&GraphicsProtocol);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): LocateProtocol\n");
return Status;
}
/* Show Home menu & Logo.bmp */
ShowHomeMenu();
Status = GraphicsProtocol->Blt(GraphicsProtocol, GopBlt, EfiBltBufferToVideo, 0, 0, x, y, BmpWidth, BmpHeight, 0); // x,y是欲顯示之Logo.bmp左上角座標
// EFI_GRAPHICS_OUTPUT_BLT_PIXEL White = { 0xFF, 0xFF, 0xFF, 0 };
// Status = GraphicsProtocol->Blt(GraphicsProtocol, &White, EfiBltVideoFill, 0, 0, x, y, 1, 1, 0);
// MicroSecondDelay(100000*10000); // Force to stay
while(1)
{
Status = gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
switch (Key.UnicodeChar)
{
case 'B': // BOOT MENU
gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
Print(L"[Press B] ENTER BOOT MENU...\n");
gST -> ConOut -> SetAttribute(gST->ConOut,0x4); // 設定字為紅色(0x04)
goto BOOTMENU;
break;
case 'S': // SETUP
gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
Print(L"[Press S] SETUP\n");
gST -> ConOut -> SetAttribute(gST->ConOut,0x2); // 設定字為綠色(0x02)
break;
case 'Q': // Q
gST -> ConOut -> ClearScreen(gST->ConOut); // 清除畫面
Print(L"[Press Q] QUIT\n");
goto SHUTDOWN;
break;
}
// MicroSecondDelay(10000*10000); // Force to stay
}
SHUTDOWN:
Print(L"--- SHUTDOWN: User press ESC ---\n");
BOOTMENU:
BootMenu();
// Free File&Buffer
gBS->FreePool(fileinfo);
gBS->FreePool(Buffer);
return EFI_SUCCESS;
}
本篇完成Entry.c
進入Boot Menu部分,也將前篇卡住的讀使用者按鍵
問題解決,接下來幾篇會完成Kernel部分。我們明天見!